﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Acme.BookStore.Authors;
using Acme.BookStore.Permissions;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Caching;
using Volo.Abp.Auditing;
using Volo.Abp;
using Volo.Abp.Features;

namespace Acme.BookStore.Books
{
    [RequiresFeature("BookStore.Author")]
    public class BookAppService : CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>, IBookAppService
    {
        IAuthorRepository authorRepository;
        IDistributedCache<BookDto, Guid> distributedCacheBook;
        public BookAppService(IRepository<Book, Guid> repository, IAuthorRepository authorRepository,IDistributedCache<BookDto, Guid> distributedCacheBook) : base(repository)
        {
            GetPolicyName = BookStorePermissions.Books.Default;
            GetListPolicyName = BookStorePermissions.Books.Default;
            CreatePolicyName = BookStorePermissions.Books.Create;
            UpdatePolicyName = BookStorePermissions.Books.Edit;
            DeletePolicyName = BookStorePermissions.Books.Delete;

            this.authorRepository = authorRepository;
            this.distributedCacheBook = distributedCacheBook;
        }

        public override async Task<BookDto> GetAsync(Guid id)
        {
            var bookDto = await distributedCacheBook.GetOrAddAsync(id, async () =>
             {
                 var query = from book in Repository
                             join author in authorRepository on book.AuthorId equals author.Id
                             where book.Id == id
                             select new { book, author.Name };
                 var data = await AsyncExecuter.FirstOrDefaultAsync(query);
                 if (data == null) throw new EntityNotFoundException(typeof(Book), id);

                 var bookDto = ObjectMapper.Map<Book, BookDto>(data.book);
                 bookDto.AuthorName = data.Name;
                 return bookDto; 
             });
            return bookDto;
        }

        public override async Task<PagedResultDto<BookDto>> GetListAsync(PagedAndSortedResultRequestDto input)
        {
            var query = from book in Repository
                        join author in authorRepository on book.AuthorId equals author.Id
                        orderby input.Sorting
                        select new { book, author.Name };
            query = query.Skip(input.SkipCount)
                         .Take(input.MaxResultCount);
            var data = await AsyncExecuter.ToListAsync(query);

            var bookdtos = data.Select(r =>
              {
                  var bookDto = ObjectMapper.Map<Book, BookDto>(r.book);
                  bookDto.AuthorName = r.Name;
                  return bookDto;
              }).ToList();
            var totalCount = await Repository.GetCountAsync();
            var ret = new PagedResultDto<BookDto>(totalCount, bookdtos);
            return ret;
        }


        public async override Task<BookDto> UpdateAsync(Guid id, CreateUpdateBookDto input)
        {
            await distributedCacheBook.RemoveAsync(id);
            return await base.UpdateAsync(id, input);
        }

        public async override Task DeleteAsync(Guid id)
        {
            await base.DeleteAsync(id);
            await distributedCacheBook.RemoveAsync(id);
        }

        [RemoteService(IsMetadataEnabled = true)]
        public async Task<ListResultDto<AuthorLookupDto>> GetAuthorLookupAsync()
        {
            var authors = await authorRepository.GetListAsync();
            return new ListResultDto<AuthorLookupDto>(
                ObjectMapper.Map<List<Author>, List<AuthorLookupDto>>(authors)
            );
        }
    }
}
